package com.yyfax.pay.third.facade.file;

import java.net.SocketTimeoutException;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.yyfax.commons.http.HttpUtil;
import com.yyfax.commons.json.FastJsonUtil;
import com.yyfax.framework.common.Log;
import com.yyfax.framework.common.OpResponse;
import com.yyfax.framework.yyfax.YYFaxSignUtil;
import com.yyfax.pay.common.constants.FuiouDepositConstant;
import com.yyfax.pay.common.enums.FuiouBusiTypeEnum;
import com.yyfax.pay.common.enums.PayModeEnum;
import com.yyfax.pay.common.enums.PayTypeEnum;
import com.yyfax.pay.common.enums.PaymentEnum;
import com.yyfax.pay.common.exception.PayExceptionCode;
import com.yyfax.pay.config.data.service.MerchantPaymentService;
import com.yyfax.pay.depository.domain.FuiouFileDownload;
import com.yyfax.pay.depository.domain.FuiouFileDownloadDetail;
import com.yyfax.pay.depository.domain.FuiouFileUpload;
import com.yyfax.pay.depository.domain.FuiouFileUploadDetail;
import com.yyfax.pay.depository.vo.FuiouDownloadFileVO;
import com.yyfax.pay.depository.vo.FuiouFileQueryVO;
import com.yyfax.pay.depository.vo.FuiouUploadFileVO;
import com.yyfax.pay.third.model.to.file.DownloadReq;
import com.yyfax.pay.third.model.to.file.FileConfigExt;
import com.yyfax.pay.third.model.to.file.QueryReq;
import com.yyfax.pay.third.model.to.file.UploadReq;

/**
 * 文件系统服务
 * 
 * @author buyi
 * @date 2017-09-06 09:42:13
 * @since v2.0.0
 */
@Service
public class FileServerService {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Resource
	private MerchantPaymentService merchantPaymentService;

	@Resource
	private FuiouDepositConstant fuiouDepositConstant;

	/**
	 * 获取文件服务配置信息
	 * 
	 * @author buyi
	 * @date 2017-09-06 10:18:07
	 * @since v2.0.0
	 * @param merchantCode
	 * @return
	 */
	private FileConfigExt getFileConfigExt(String merchantCode, PayTypeEnum payTypeEnum) {
		FileConfigExt configExt = null;
		try {
			configExt = merchantPaymentService.queryByThirdMerchantCodeForExt(merchantCode, PaymentEnum.FIEL_SERVER.getValue(),
					payTypeEnum.getValue(), PayModeEnum.AUTH_PAY.getValue(), FileConfigExt.class);
		} catch (Exception e) {
			logger.error(Log.op("FileServerService.getFileConfigExt").msg("获取文件服务配置信息异常").kv("merchantCode", merchantCode).toString(), e);
			throw PayExceptionCode.ERROR_MERCHANT_CONFIG_ILLEGAL.exp();
		}

		if (configExt == null) {
			logger.error(Log.op("FileServerService.getFileConfigExt").msg("获取文件服务配置信息为空").kv("merchantCode", merchantCode).toString());
			throw PayExceptionCode.ERROR_MERCHANT_CONFIG_ILLEGAL.exp();
		}

		return configExt;
	}

	/**
	 * 上文文件到文件系统<br>
	 * 
	 * @author buyi
	 * @date 2017-10-03 16:44:59
	 * @since v2.0.0
	 * @param vo
	 * @return 返回响应码，该响应码已经转换成支付模块的响应码
	 */
	public OpResponse doUpload(FuiouUploadFileVO vo) {
		final String op = "FileServerService.doUpload";

		if (vo == null) {
			logger.warn(Log.op(op).msg("参数vo为空").toString());
			return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
		}

		FuiouFileUpload po = vo.getPo();

		List<FuiouFileUploadDetail> list = vo.getList();

		// 上传文件到文件服务请求参数
		UploadReq req = new UploadReq();
		// 文件名集合
		List<String> fileName = null;

		try {
			// 提取文件名集合
			fileName = Lists.transform(list, new Function<FuiouFileUploadDetail, String>() {
				@Override
				public String apply(FuiouFileUploadDetail input) {
					return input.getFileName();
				}
			});

			PayTypeEnum payType = FuiouBusiTypeEnum.FILE_TRADE_UPLOAD.isValue(po.getType()) ? PayTypeEnum.FILE_TRADE : PayTypeEnum.FILE_CHECK;

			// 获取异步通知地址
			String notifyUrl = FuiouBusiTypeEnum.FILE_TRADE_UPLOAD.isValue(po.getType())
					? fuiouDepositConstant.getFileTradeUploadNotifyUrl(po.getMerchantCode())
					: fuiouDepositConstant.getFileCheckUploadNotifyUrl(po.getMerchantCode());

			FileConfigExt fileConfigExt = getFileConfigExt(po.getMerchantCode(), payType);

			if (StringUtils.isBlank(fileConfigExt.getUploadUrl())) {
				logger.error(Log.op(op).msg("获取文件服务配置，uploadUrl缺失").kv("merchantCode", po.getMerchantCode()).toString());
				throw PayExceptionCode.ERROR_MERCHANT_CONFIG_ILLEGAL.exp();
			}

			req.setOrderId(po.getBusiId());
			req.setLocalDirectory(fileConfigExt.getLocalRootDir() + "/" + po.getOrigDir());
			req.setRemoteDirectory(fileConfigExt.getRemoteRootDir() + "/" + po.getDestDir());
			req.setBusinessType(fileConfigExt.getBusiType()); // 交易
			req.setNotifyUrl(notifyUrl); // 异步通知地址
			req.setFileNames(fileName);
			req.setProtocol(fileConfigExt.getProtocol());
			req.setHost(fileConfigExt.getHost());
			req.setPort(fileConfigExt.getPort());
			//暂时先这么解决脱敏问题
			String logBody = JSON.toJSONString(req);

			req.setName(fileConfigExt.getName());
			req.setPassword(fileConfigExt.getPassword());
			String body = JSON.toJSONString(req);

			logger.info(Log.op(op).msg("文件上传 | 请求文件服务参数").kv("busiId", po.getBusiId()).kv("uploadUrl", fileConfigExt.getUploadUrl()).kv("body", logBody)
					.toString());

			String rspStr = HttpUtil.postBodyDirect(fileConfigExt.getUploadUrl(), body, null, null, null, null, null,
					fileConfigExt.getConnectTimeOut(), fileConfigExt.getReadTimeOut());

			logger.info(Log.op(op).msg("文件上传 | 请求文件服务响应参数").kv("busiId", po.getBusiId()).kv("rsp", rspStr).toString());

			if (StringUtils.isBlank(rspStr)) {
				logger.error(Log.op(op).msg("上传文件到文件服务器，响应返回null").kv("busiId", po.getBusiId()).toString());
				return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
			}

			OpResponse response = FastJsonUtil.fromJson(rspStr, OpResponse.class);
			// 转换文件系统的响应码，目前将文件系统非成功响应都认为是失败
			if (PayExceptionCode.SUCCESS.isCode(response.getCode())) {
				return OpResponse.success("成功");
			}
		} catch (SocketTimeoutException e) {
			logger.error(Log.op(op).msg("上传文件到文件服务请求超时").kv("busiId", po.getBusiId()).toString(), e);
			// 如果请求超时，则返回超时响应码
			return OpResponse.fail(PayExceptionCode.WARN_REQ_TIME_OUT.getCode(), PayExceptionCode.WARN_REQ_TIME_OUT.getDesc(), null);
		} catch (Exception e) {
			logger.error(Log.op(op).msg("上传文件到文件服务出现未知异常").kv("busiId", po.getBusiId()).toString(), e);
		} finally {
			req = null;
			fileName = null;
		}

		return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
	}

	/**
	 * 下载
	 * 
	 * @author buyi
	 * @date 2017-09-06 19:57:06
	 * @since v2.0.0
	 * @param vo
	 * @return
	 */
	public OpResponse doDownload(FuiouDownloadFileVO vo) {
		final String op = "FileServerService.doDownload";

		if (vo == null) {
			logger.warn(Log.op(op).msg("参数vo为空").toString());
			return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
		}

		FuiouFileDownload po = vo.getPo();

		List<FuiouFileDownloadDetail> list = vo.getList();

		DownloadReq req = new DownloadReq();

		List<String> fileName = null;
		try {
			// 提取文件名集合
			fileName = Lists.transform(list, new Function<FuiouFileDownloadDetail, String>() {
				@Override
				public String apply(FuiouFileDownloadDetail input) {
					return input.getFileName();
				}
			});

			PayTypeEnum payType = PayTypeEnum.FILE_TRADE;
			// 异步通知地址
			String notifyUrl = "";
			if (FuiouBusiTypeEnum.FILE_TRADE_DOWNLOAD.isValue(po.getType())) {
				// 如果是交易下载类型
				payType = PayTypeEnum.FILE_TRADE;
				notifyUrl = fuiouDepositConstant.getFileTradeDownloadNotifyUrl(po.getMerchantCode());
			} else if (FuiouBusiTypeEnum.FILE_CHECK_DOWNLOAD.isValue(po.getType())) {
				// 如果是报备
				payType = PayTypeEnum.FILE_CHECK;
				notifyUrl = fuiouDepositConstant.getFileCheckDownloadNotifyUrl(po.getMerchantCode());
			} else if (FuiouBusiTypeEnum.FILE_ACCOUNT_DOWNLOAD.isValue(po.getType())) {
				// 如果是对账下载
				payType = PayTypeEnum.FILE_ACCOUNT;
				notifyUrl = fuiouDepositConstant.getFileAccountDownloadNotifyUrl(po.getMerchantCode());
			} else {
				logger.error(Log.op(op).msg("文件下载 | 未知的业务类型").kv("busiId", po.getBusiId()).kv("type", po.getType()).toString());
				return OpResponse.fail(PayExceptionCode.SYS_ERROR.getCode(), PayExceptionCode.SYS_ERROR.getDesc(), null);
			}

			FileConfigExt fileConfigExt = getFileConfigExt(po.getMerchantCode(), payType);

			if (StringUtils.isBlank(fileConfigExt.getDownloadUrl())) {
				logger.error(Log.op(op).msg("获取文件服务配置，downloadUrl缺失").kv("merchantCode", po.getMerchantCode()).toString());
				throw PayExceptionCode.ERROR_MERCHANT_CONFIG_ILLEGAL.exp();
			}

			// 封装请求实体
			req.setOrderId(po.getBusiId());
			req.setLocalDirectory(fileConfigExt.getLocalRootDir() + "/" + po.getDestDir());
			req.setRemoteDirectory(fileConfigExt.getRemoteRootDir() + "/" + po.getOrigDir());
			req.setBusinessType(fileConfigExt.getBusiType());
			req.setNotifyUrl(notifyUrl); // 异步通知地址
			req.setFileNames(fileName);
			req.setProtocol(fileConfigExt.getProtocol());
			req.setHost(fileConfigExt.getHost());
			req.setPort(fileConfigExt.getPort());
			//暂时先这么解决脱敏问题
			String logBody = JSON.toJSONString(req);

			req.setName(fileConfigExt.getName());
			req.setPassword(fileConfigExt.getPassword());
			String body = JSON.toJSONString(req);


			logger.info(Log.op(op).msg("文件下载 | 请求文件服务参数").kv("busiId", po.getBusiId()).kv("downloadUrl", fileConfigExt.getDownloadUrl())
					.kv("body", logBody).toString());

			String rspStr = HttpUtil.postBodyDirect(fileConfigExt.getDownloadUrl(), body, null, null, null, null, null,
					fileConfigExt.getConnectTimeOut(), fileConfigExt.getReadTimeOut());

			logger.info(Log.op(op).msg("文件下载 | 请求文件服务参数").kv("busiId", po.getBusiId()).kv("rsp", rspStr).toString());

			if (StringUtils.isBlank(rspStr)) {
				logger.error(Log.op(op).msg("下载文件，响应返回null").kv("busiId", po.getBusiId()).toString());
				return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
			}

			OpResponse response = FastJsonUtil.fromJson(rspStr, OpResponse.class);
			// 转换文件系统的响应码，目前将文件系统非成功响应都认为是失败
			if (PayExceptionCode.SUCCESS.isCode(response.getCode())) {
				return OpResponse.success("成功");
			}
		} catch (SocketTimeoutException e) {
			logger.error(Log.op(op).msg("下载文件到文件服务请求超时").kv("busiId", po.getBusiId()).toString(), e);
			// 如果请求超时，则返回超时响应码
			return OpResponse.fail(PayExceptionCode.WARN_REQ_TIME_OUT.getCode(), PayExceptionCode.WARN_REQ_TIME_OUT.getDesc(), null);
		} catch (Exception e) {
			logger.error(Log.op(op).msg("下载文件到文件服务出现未知异常").kv("busiId", po.getBusiId()).toString(), e);
		} finally {
			req = null;
			fileName = null;
		}

		return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
	}

	/**
	 * 校验文件服务签名
	 * 
	 * @author buyi
	 * @date 2017-09-15 15:01:28
	 * @since v2.0.0
	 * @param merchantCode
	 * @param sign
	 * @param body
	 * @return false 表示验签不通过，true表示验签通过
	 */
	public boolean validFileSign(String merchantCode, String sign, String body, PayTypeEnum payTypeEnum) {
		FileConfigExt fileConfigExt = getFileConfigExt(merchantCode, payTypeEnum);
		return YYFaxSignUtil.checkHeaderSign(fileConfigExt.getKey(), body, sign);
	}

	/**
	 * 查询回盘文件
	 * 
	 * @author buyi
	 * @date 2017-10-09 11:13:27
	 * @since v2.0.0
	 * @param params
	 * @param fuiouBusiTypeEnum
	 * @return
	 */
	public OpResponse query(FuiouFileQueryVO vo) {
		final String op = "FileServerService.query";

		try {

			FileConfigExt fileConfigExt = getFileConfigExt(vo.getMerchantCode(), vo.getType());
			if (StringUtils.isBlank(fileConfigExt.getQueryUrl())) {
				logger.error(Log.op(op).msg("获取文件服务配置，queryUrl缺失").kv("merchantCode", vo.getMerchantCode()).toString());
				throw PayExceptionCode.ERROR_MERCHANT_CONFIG_ILLEGAL.exp();
			}

			// 获取异步通知地址
			// String notifyUrl = PayTypeEnum.FILE_TRADE.equals(vo.getType()) ? fuiouDepositConstant.getFileTradeQueryUrl(vo.getMerchantCode())
			// : fuiouDepositConstant.getFileCheckQueryUrl(vo.getMerchantCode());
			String notifyUrl = PayTypeEnum.FILE_TRADE.isValue(vo.getType().getValue())
					? fuiouDepositConstant.getFileTradeQueryUrl(vo.getMerchantCode())
					: fuiouDepositConstant.getFileCheckQueryUrl(vo.getMerchantCode());

			QueryReq req = new QueryReq();
			req.setOrderId(vo.getBusiId());
			req.setNotifyUrl(notifyUrl);
			req.setRemoteDirectory(fileConfigExt.getRemoteRootDir() + "/" + vo.getDestDir());
			req.setBusinessType(fileConfigExt.getBusiType());
			req.setFileNames(vo.getFiles());
			req.setProtocol(fileConfigExt.getProtocol());
			req.setHost(fileConfigExt.getHost());
			req.setPort(fileConfigExt.getPort());
			//暂时先这么解决脱敏问题
			String logBody = JSON.toJSONString(req);

			req.setName(fileConfigExt.getName());
			req.setPassword(fileConfigExt.getPassword());
			String body = JSON.toJSONString(req);

			logger.info(Log.op(op).msg("文件查询 | 请求文件服务参数").kv("orderId", vo.getOrderId()).kv("downloadUrl", fileConfigExt.getDownloadUrl())
					.kv("body", logBody).toString());

			String rspStr = HttpUtil.postBodyDirect(fileConfigExt.getQueryUrl(), body, null, null, null, null, null,
					fileConfigExt.getConnectTimeOut(), fileConfigExt.getReadTimeOut());

			logger.info(Log.op(op).msg("文件查询 | 请求文件服务参数").kv("orderId", vo.getOrderId()).kv("rsp", rspStr).toString());

			if (StringUtils.isBlank(rspStr)) {
				logger.error(Log.op(op).msg("文件查询，响应返回null").kv("orderId", vo.getOrderId()).toString());
				return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
			}

			OpResponse response = FastJsonUtil.fromJson(rspStr, OpResponse.class);
			// 转换文件系统的响应码，目前将文件系统非成功响应都认为是失败
			if (PayExceptionCode.SUCCESS.isCode(response.getCode())) {
				return OpResponse.success("成功");
			}
		} catch (SocketTimeoutException e) {
			logger.error(Log.op(op).msg("文件查询请求超时").kv("orderId", vo.getOrderId()).toString(), e);
			// 如果请求超时，则返回超时响应码
			return OpResponse.fail(PayExceptionCode.WARN_REQ_TIME_OUT.getCode(), PayExceptionCode.WARN_REQ_TIME_OUT.getDesc(), null);
		} catch (Exception e) {
			logger.error(Log.op(op).msg("文件查询出现未知异常").kv("orderId", vo.getOrderId()).toString(), e);
		}

		return OpResponse.fail(PayExceptionCode.ERROR_FILE_FAIL.getCode(), PayExceptionCode.ERROR_FILE_FAIL.getDesc(), null);
	}

}
